#ifndef __H_STREAM_DATA_H__
#define __H_STREAM_DATA_H__

#include <string>
#include <string.h>
#include <exception>

class StreamDataReader;
class StreamDataWriter;
class StreamData
{
public:
    ~StreamData() = default;
    StreamData() = default;
    StreamData(const char* data, size_t size)
    {
        m_data.assign(data, size);
    }
    StreamData(const std::string& data)
    {
        m_data = data;
    }
    StreamData(std::string&& data)
    {
        m_data = std::move(data);
    }
    StreamData(const StreamData& data)
    {
        m_data = data.m_data;
    }
    StreamData(StreamData&& data)
    {
        m_data = std::move(data.m_data);
    }

    inline StreamData& operator = (const std::string& data)
    {
        m_data = data;
        return *this;
    }
    inline StreamData& operator = (std::string&& data)
    {
        m_data = std::move(data);
        return *this;
    }
    inline StreamData& operator = (const StreamData& data)
    {
        m_data = data.m_data;
        return *this;
    }
    inline StreamData& operator = (StreamData&& data)
    {
        m_data = std::move(data.m_data);
        return *this;
    }

    inline const std::string& Data() const
    {
        return m_data;
    }
    inline std::string& Data()
    {
        return m_data;
    }

    inline bool Copy(const void* pInBuf, int nLen)
    {
        m_data.assign(static_cast<const char*>(pInBuf), nLen);
        return true;
    }

protected:
    std::string m_data = "";
};

class StreamDataReader
{
private:
    StreamData& m_data;
    char* start_pos = NULL;
    char* read_pos = NULL;
    char* end_pos = NULL;

private:
    inline int Read(void* addr, int size)
    {
        if (size > int(end_pos - read_pos))
        {
            return 0;
        }
        bcopy(read_pos, addr, size);
        read_pos += size;
        return size;
    }

public:
    explicit StreamDataReader(StreamData& data) : m_data(data)
    {
        auto& d = m_data.Data();
        start_pos = read_pos = const_cast<char*>(d.data());
        end_pos = start_pos + d.size();
    }
    ~StreamDataReader()
    {
        //
    }

    template<typename T, class = typename std::enable_if<std::is_pod<T>::value>::type>
    inline StreamDataReader& operator >> (T& t)
    {
        Read(&t, sizeof(t));
        return *this;
    }
    inline StreamDataReader& operator >> (std::string& t)
    {
        int size = 0;
        if (sizeof(size) != Read(&size, sizeof(size)))
        {
            return *this;
        }
        if (size > (end_pos - read_pos))
        {
            return *this;
        }
        t.resize(size);
        Read(const_cast<char*>(t.data()), size);
        return *this;
    }

public:
    template<typename T, class = typename std::enable_if<std::is_pod<T>::value>::type>
    inline T Read()
    {
        T v; (*this) >> v; return v;
    }
    inline bool ReadBool(void)
    {
        bool v; (*this) >> v; return v;
    }
    inline std::int32_t ReadInt(void)
    {
        std::int32_t v; (*this) >> v; return v;
    }
    inline std::int64_t ReadInt64(void)
    {
        std::int64_t v; (*this) >> v; return v;
    }
    inline unsigned long ReadULong(void)
    {
        unsigned long v; (*this) >> v; return v;
    }
    inline short ReadShort(void)
    {
        short v; (*this) >> v; return v;
    }
    inline uint8_t ReadByte(void)
    {
        uint8_t v; (*this) >> v; return v;
    }
    inline std::string ReadString(void)
    {
        std::string v; (*this) >> v; return v;
    }
    inline int ReadBinary(char* pBuf, int nMaxLen)
    {
        return Read(pBuf, nMaxLen);
    }
    inline void Reset(void)
    {
        read_pos = start_pos;
    }

};

class StreamDataWriter
{
private:
    StreamData& m_data;

private:
    inline int Write(const void* addr, int size)
    {
        m_data.Data().append((const char*)addr, size);
        return size;
    }

public:
    explicit StreamDataWriter(StreamData& data) : m_data(data)
    {
    }
    ~StreamDataWriter()
    {
        //
    }

    template<typename T, class = typename std::enable_if<std::is_pod<T>::value>::type>
    inline StreamDataWriter& operator << (const T& t)
    {
        Write(&t, sizeof(t));
        return *this;
    }
    inline StreamDataWriter& operator << (const std::string& t)
    {
        int size = t.size();
        Write(&size, sizeof(size));
        Write(const_cast<char*>(t.data()), size);
        return *this;
    }

public:
    template<typename T, class = typename std::enable_if<std::is_pod<T>::value>::type>
    inline bool Write(const T& nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteBool(bool nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteInt(std::int32_t nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteInt64(std::int64_t nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteByte(uint8_t nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteShort(short nValue)
    {
        *this << nValue; return true;
    }
    inline bool WriteString(const std::string& strDate)
    {
        *this << strDate; return true;
    }
    inline bool WriteBinary(const char* pBuf, int nLen)
    {
        return Write(const_cast<char*>(pBuf), nLen) == nLen;
    }
    inline bool WriteBP(const char* pBuf, int nLen)
    {
        return Write(const_cast<char*>(pBuf), nLen) == nLen;
    }

};

template <typename T>
class StreamRef
{
public:
    template<typename... Args>
    explicit StreamRef(Args... args)
    {
        m_data.resize(sizeof(T));
        T* t = static_cast<T*>(static_cast<void*>(const_cast<char*>(m_data.data())));
        new (t)T(args...);
    }
    ~StreamRef()
    {
        if (IsValid())
        {
            T* t = static_cast<T*>(static_cast<void*>(const_cast<char*>(m_data.data())));
            t->~T();
            t = nullptr;
        }
    }

public:
    const T* operator->() const
    {
        if (!IsValid())
        {
            throw "const T* StreamRef<T>::operator->() object is nullptr";
        }
        T* t = static_cast<T*>(static_cast<void*>(const_cast<char*>(m_data.data())));
        return t;
    }
    T* operator->()
    {
        if (!IsValid())
        {
            throw "T* StreamRef<T>::operator->() object is nullptr";
        }
        T* t = static_cast<T*>(static_cast<void*>(const_cast<char*>(m_data.data())));
        return t;
    }
public:
    bool CopyFrom(StreamData&& data)
    {
        if (data.Data().size() != sizeof(T))
        {
            return false;
        }
        m_data = std::move(data.Data());
        return true;
    }
    void CopyTo(StreamData& data)
    {
        data.Data() = std::move(m_data);
        m_data.clear();
    }
    StreamData&& ToStreamData()
    {
        StreamData data;
        data.Data() = std::move(m_data);
        m_data.clear();
        return std::move(data);
    }

public:
    inline bool IsValid()
    {
        return m_data.size() == sizeof(T);
    }

private:
    std::string m_data;
};


template<typename T, class = typename std::enable_if<std::is_pod<T>::value>::type>
class StreamRefPod : public StreamRef<T>
{
public:
    template<typename... Args>
    explicit StreamRefPod(Args... args):StreamRef<T>(args...)
    {
    }
    ~StreamRefPod()
    {
    }
};

#endif